/**
 * This file is Copyright © 2008 Software Craftsmen Limited. All Rights Reserved.
 */
package com.softwarecraftsmen.orogeny.properties;

import com.softwarecraftsmen.orogeny.BuildScript.BuildSyntaxException;
import com.softwarecraftsmen.orogeny.buildEnvironments.BuildEnvironment;
import static com.softwarecraftsmen.orogeny.properties.PropertyName.propertyName;
import org.jetbrains.annotations.NotNull;

import java.io.StringWriter;
import static java.lang.String.format;
import java.util.ArrayList;
import java.util.List;
import static java.util.Locale.UK;

public class PropertyExpression
{
	private final String formatString;
	private final List<PropertyName> formatStringArguments;

	public PropertyExpression(final @NotNull String formatString, final @NotNull List<PropertyName> formatStringArguments)
	{
		this.formatString = formatString;
		this.formatStringArguments = formatStringArguments;
	}

	@NotNull
	public String evaluate(final @NotNull BuildEnvironment buildEnvironment)
	{
		final Object[] values = new Object[formatStringArguments.size()];
		for (int index = 0; index < values.length; index++)
		{
			values[index] = formatStringArguments.get(index).evaluate(buildEnvironment);
		}
		return format(UK, formatString, values);
	}

	@NotNull
	public static PropertyExpression parseExpression(final @NotNull String expression)
	{
		final StringWriter formatStringWriter = new StringWriter();
		StringWriter formatStringArgumentWriter = null;
		boolean isParsingPropertyName = false;
		final List<PropertyName> formatStringArguments = new ArrayList<>();
		for (int index = 0; index < expression.length(); index++)
		{
			final char character = expression.charAt(index);
			final Character nextCharacter = (index + 1 < expression.length()) ? expression.charAt(index + 1) : null;
			if (isParsingPropertyName)
			{
				if (closePropertyName(character))
				{
					if (nextCharacter != null && closePropertyName(nextCharacter))
					{
						formatStringWriter.write(character);
						index++;
					}
					else
					{
						isParsingPropertyName = false;

						formatStringArguments.add(propertyName(formatStringArgumentWriter.toString()));
						formatStringArgumentWriter = null;
					}
				}
				else
				{
					formatStringArgumentWriter.write(character);
				}
			}
			else
			{
				if (closePropertyName(character))
				{
					if (nextCharacter != null && closePropertyName(nextCharacter))
					{
						formatStringWriter.write(character);
						index++;
					}
				}
				else if (openPropertyName(character))
				{
					if (nextCharacter != null && openPropertyName(nextCharacter))
					{
						formatStringWriter.write(character);
						index++;
					}
					else
					{
						isParsingPropertyName = true;

						formatStringArgumentWriter = new StringWriter();

						formatStringWriter.write("%");
						formatStringWriter.write(String.valueOf(formatStringArguments.size() + 1));
						formatStringWriter.write("$s");
					}
				}
				else
				{
					formatStringWriter.write(character);
				}
			}
		}
		if (isParsingPropertyName)
		{
			throw new DynamicPropertyNameExpressionContainsIncorrectlyEscapedPropertyNameException(expression);
		}
		return new PropertyExpression(formatStringWriter.toString(), formatStringArguments);
	}

	private static boolean openPropertyName(final char character) {return character == '{';}

	private static boolean closePropertyName(final char character) {return character == '}';}

	public static final class DynamicPropertyNameExpressionContainsIncorrectlyEscapedPropertyNameException extends BuildSyntaxException
	{
		public DynamicPropertyNameExpressionContainsIncorrectlyEscapedPropertyNameException(final @NotNull String expression)
		{
			super(format(UK, "The expression '%1$s' does not have a properly escaped defineProperty name in it (ie matching numbers of { and })", expression));
		}
	}
}
